<!DOCTYPE HTML>
<!--
	Dimension by HTML5 UP
	html5up.net | @ajlkn
	Free for personal and commercial use under the CCA 3.0 license (html5up.net/license)
-->
<html>
 <head>
  <title>
   Dimension by HTML5 UP
  </title>
  <!-- <meta charset="utf-8" /> -->
  <!-- <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" /> -->
  <meta charset="utf-8"/>
  <meta content="width=device-width,initial-scale=1.0" name="viewport"/>
  <link href="../../assets/css/article.css" rel="stylesheet"/>
  <link href="https://cdn.bootcss.com/highlight.js/9.15.8/styles/github.min.css" rel="stylesheet"/>
  <noscript>
   <link href="../../assets/css/noscript.css" rel="stylesheet"/>
  </noscript>
 </head>
 <body>
  <div id="app">
  </div>
  <!-- built files will be auto injected -->
 </body>
 <body class="is-preload">
  <!-- Wrapper -->
  <div id="wrapper">
   <!-- Main -->
   <div id="main">
    <article id="article">
     <h1 id="spring-cloud-gateway-global-filter">
      Spring Cloud Gateway (六) 自定义 Global Filter
     </h1>
     <hr/>
     <h2 id="_1">
      简介
     </h2>
     <p>
      在前面五篇的分析中，对 Spring Cloud Gateway 的 filter 组件有了一个大概的认知，今天就练练手，写一个统计请求返回时长的 global filter
     </p>
     <h2 id="_2">
      思路整理
     </h2>
     <p>
      阅读官方文档和在前面读源码的过程中，大致知道 global filter 需要继承 GlobalFilter 、 Ordered
     </p>
     <ul>
      <li>
       GlobalFilter : 需要重写 filter 方法，在其中实现自己的处理逻辑，很大部分都是对 exchange 进行操作，取值赋值等等
      </li>
      <li>
       Ordered ： 需要重写 getOrder 方法，决定自定义 filter 在链中的位置，按照数字大小进行的排序
      </li>
     </ul>
     <h3 id="filter">
      自定义时长统计 filter 编写
     </h3>
     <p>
      这里就简单的借鉴下 GatewayMetricsFilter 的写法，在 filter 链触发完成后，无论失败或者成功，都进行统计。filter 次序就简单的取 WRITE_RESPONSE_FILTER_ORDER 之后（用它定义的次序减一）。代码大致如下：
     </p>
     <div class="codehilite">
      <pre><span></span><code><span class="cm">/**</span>
<span class="cm"> * 统计一次请求在filter链中的时长</span>
<span class="cm"> *</span>
<span class="cm"> * @author lw1243925457</span>
<span class="cm"> */</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">DurationStatisticsFilter</span> <span class="kd">implements</span> <span class="n">GlobalFilter</span><span class="p">,</span> <span class="n">Ordered</span> <span class="p">{</span>

    <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="n">Log</span> <span class="n">log</span> <span class="o">=</span> <span class="n">LogFactory</span><span class="p">.</span><span class="na">getLog</span><span class="p">(</span><span class="n">DurationStatisticsFilter</span><span class="p">.</span><span class="na">class</span><span class="p">);</span>
    <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="n">String</span> <span class="n">START_STAMP</span> <span class="o">=</span> <span class="s">"startStamp"</span><span class="p">;</span>

    <span class="cm">/**</span>
<span class="cm">     * 请求响应时长统计</span>
<span class="cm">     * 1.首先将请求进来时的时间戳写入到 exchange中</span>
<span class="cm">     * 2.filter链走完后，无论成功或者失败，都视为完成，从exchange取出开始时间戳，打印时长信息</span>
<span class="cm">     * @param exchange the current server exchange</span>
<span class="cm">     * @param chain provides a way to delegate to the next filter</span>
<span class="cm">     * @return mono</span>
<span class="cm">     */</span>
    <span class="nd">@Override</span>
    <span class="kd">public</span> <span class="n">Mono</span><span class="o">&lt;</span><span class="n">Void</span><span class="o">&gt;</span> <span class="nf">filter</span><span class="p">(</span><span class="n">ServerWebExchange</span> <span class="n">exchange</span><span class="p">,</span> <span class="n">GatewayFilterChain</span> <span class="n">chain</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">log</span><span class="p">.</span><span class="na">debug</span><span class="p">(</span><span class="s">"duration statistics filter"</span><span class="p">);</span>

        <span class="n">exchange</span><span class="p">.</span><span class="na">getAttributes</span><span class="p">().</span><span class="na">put</span><span class="p">(</span><span class="n">START_STAMP</span><span class="p">,</span> <span class="n">System</span><span class="p">.</span><span class="na">currentTimeMillis</span><span class="p">());</span>
        <span class="k">return</span> <span class="n">chain</span><span class="p">.</span><span class="na">filter</span><span class="p">(</span><span class="n">exchange</span><span class="p">)</span>
                <span class="p">.</span><span class="na">doFinally</span><span class="p">(</span><span class="n">f</span> <span class="o">-&gt;</span> <span class="n">printDurationTime</span><span class="p">(</span><span class="n">exchange</span><span class="p">));</span>
    <span class="p">}</span>

    <span class="kd">private</span> <span class="kt">void</span> <span class="nf">printDurationTime</span><span class="p">(</span><span class="n">ServerWebExchange</span> <span class="n">exchange</span><span class="p">)</span> <span class="p">{</span>
        <span class="kt">long</span> <span class="n">startStamp</span> <span class="o">=</span> <span class="n">exchange</span><span class="p">.</span><span class="na">getAttribute</span><span class="p">(</span><span class="n">START_STAMP</span><span class="p">);</span>
        <span class="kt">long</span> <span class="n">endStamp</span> <span class="o">=</span> <span class="n">System</span><span class="p">.</span><span class="na">currentTimeMillis</span><span class="p">();</span>
        <span class="n">log</span><span class="p">.</span><span class="na">debug</span><span class="p">(</span><span class="s">"duration filter time : "</span> <span class="o">+</span> <span class="p">(</span><span class="n">endStamp</span> <span class="o">-</span> <span class="n">startStamp</span><span class="p">)</span> <span class="o">+</span> <span class="s">" ms"</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="cm">/**</span>
<span class="cm">     * 这里简单位于 NettyWriteResponseFilter 之后吧</span>
<span class="cm">     * @return order</span>
<span class="cm">     */</span>
    <span class="nd">@Override</span>
    <span class="kd">public</span> <span class="kt">int</span> <span class="nf">getOrder</span><span class="p">()</span> <span class="p">{</span>
        <span class="k">return</span> <span class="n">WRITE_RESPONSE_FILTER_ORDER</span> <span class="o">-</span> <span class="mi">1</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre>
     </div>
     <h3 id="_3">
      主函数配置运行
     </h3>
     <p>
      需要在主函数中配置 bean，整个代码大致如下：
     </p>
     <div class="codehilite">
      <pre><span></span><code><span class="nd">@SpringBootApplication</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">Application</span> <span class="p">{</span>

    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="p">(</span><span class="n">String</span><span class="o">[]</span> <span class="n">args</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">SpringApplication</span><span class="p">.</span><span class="na">run</span><span class="p">(</span><span class="n">Application</span><span class="p">.</span><span class="na">class</span><span class="p">,</span> <span class="n">args</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="nd">@Bean</span>
    <span class="kd">public</span> <span class="n">RouteLocator</span> <span class="nf">myRoutes</span><span class="p">(</span><span class="n">RouteLocatorBuilder</span> <span class="n">builder</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">return</span> <span class="n">builder</span><span class="p">.</span><span class="na">routes</span><span class="p">()</span>
                <span class="p">.</span><span class="na">route</span><span class="p">(</span><span class="n">p</span> <span class="o">-&gt;</span> <span class="n">p</span>
                        <span class="p">.</span><span class="na">path</span><span class="p">(</span><span class="s">"/"</span><span class="p">)</span>
                        <span class="p">.</span><span class="na">filters</span><span class="p">(</span><span class="n">f</span> <span class="o">-&gt;</span> <span class="n">f</span>
                                <span class="p">.</span><span class="na">addRequestParameter</span><span class="p">(</span><span class="s">"test"</span><span class="p">,</span> <span class="s">"test"</span><span class="p">)</span>
                                <span class="p">.</span><span class="na">addResponseHeader</span><span class="p">(</span><span class="s">"return"</span><span class="p">,</span> <span class="s">"return"</span><span class="p">)</span>
                                <span class="p">.</span><span class="na">retry</span><span class="p">(</span><span class="n">retryConsumer</span><span class="p">)</span>
                        <span class="p">)</span>
                        <span class="p">.</span><span class="na">uri</span><span class="p">(</span><span class="s">"http://localhost:8082/"</span><span class="p">))</span>
                <span class="p">.</span><span class="na">build</span><span class="p">();</span>
    <span class="p">}</span>

    <span class="nd">@Bean</span>
    <span class="kd">public</span> <span class="n">GlobalFilter</span> <span class="nf">durationStatisticsFilter</span><span class="p">()</span> <span class="p">{</span>
        <span class="k">return</span> <span class="k">new</span> <span class="n">DurationStatisticsFilter</span><span class="p">();</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre>
     </div>
     <p>
      很简单，大致就这些，运行起来，浏览器访问，可以看到下面的输出，over
     </p>
     <div class="codehilite">
      <pre><span></span><code>o.s.c.g.sample.DurationStatisticsFilter  : duration statistics filter
o.s.c.g.sample.DurationStatisticsFilter  : duration filter <span class="nb">time</span> : <span class="m">349</span> ms
</code></pre>
     </div>
     <h2 id="filter_1">
      Filter 相关分析记录
     </h2>
     <ul>
      <li>
       <a href="https://github.com/lw1243925457/SE-Notes/blob/master/profession/program/java/spring/springcloudGateway/SpringCloudGateway%E6%A6%82%E8%A7%88.md">
        Spring cloud Gateway (一) 代码拉取与运行示例
       </a>
      </li>
      <li>
       <a href="https://github.com/lw1243925457/SE-Notes/blob/master/profession/program/java/spring/springcloudGateway/%E6%B5%81%E7%A8%8B%E7%B1%BB.md">
        Spring cloud Gateway（二） 一个Http请求的流程解析
       </a>
      </li>
      <li>
       <a href="https://github.com/lw1243925457/SE-Notes/blob/master/profession/program/java/spring/springcloudGateway/Filter%E9%93%BE.md">
        Spring Cloud Gateway （三） Filter 链
       </a>
      </li>
      <li>
       <a href="https://github.com/lw1243925457/SE-Notes/blob/master/profession/program/java/spring/springcloudGateway/%E9%87%8D%E8%AF%95%E6%9C%BA%E5%88%B6.md">
        Spring Cloud Gateway(四) 请求重试机制
       </a>
      </li>
      <li>
       <a href="https://github.com/lw1243925457/SE-Notes/blob/master/profession/program/java/spring/springcloudGateway/%E9%99%90%E6%B5%81.md">
        Spring Cloud Gateway(五) 限流
       </a>
      </li>
      <li>
       <a href="https://github.com/lw1243925457/SE-Notes/blob/master/profession/program/java/spring/springcloudGateway/%E8%87%AA%E5%AE%9A%E4%B9%89GlobalFilter.md">
        Spring Cloud Gateway (六) 自定义 Global Filter
       </a>
      </li>
     </ul>
    </article>
   </div>
   <!-- Footer -->
   <footer id="footer">
    <p class="copyright">
     © Untitled. Design:
     <a href="https://html5up.net">
      HTML5 UP
     </a>
     .
    </p>
   </footer>
  </div>
  <!-- BG -->
  <div id="bg">
  </div>
  <!-- Scripts -->
  <script src="../assets/js/jquery.min.js">
  </script>
  <script src="../assets/js/browser.min.js">
  </script>
  <script src="../assets/js/breakpoints.min.js">
  </script>
  <script src="../assets/js/util.js">
  </script>
  <script src="../assets/js/main.js">
  </script>
 </body>
</html>
